package pl.edu.mimuw.mas.agent.behaviour.hunting.dog;

import jade.core.AID;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.SearchConstraints;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.lang.acl.UnreadableException;

import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import pl.edu.mimuw.mas.agent.DeerAgent;
import pl.edu.mimuw.mas.agent.DogAgent;
import pl.edu.mimuw.mas.agent.behaviour.ServerBehaviour;
import pl.edu.mimuw.mas.message.ScareMessage;
import pl.edu.mimuw.mas.message.ScareRequestMessage;
import pl.edu.mimuw.mas.util.Position;
import pl.edu.mimuw.mas.util.PositionHelper;
import pl.edu.mimuw.mas.util.Time;

public class ScareServerBehaviour extends ServerBehaviour {

	private static final long serialVersionUID = 994872325416988698L;

	protected final Logger logger = LoggerFactory.getLogger(getClass());

	public ScareServerBehaviour(DogAgent a) {
		super(a);

		MessageTemplate mt = MessageTemplate.and(MessageTemplate
				.MatchConversationId(DogAgent.SCARE_CONV), MessageTemplate
				.MatchPerformative(ACLMessage.REQUEST));
		setMessageTemplate(mt);
	}

	@Override
	public boolean doAction(ACLMessage msg, ACLMessage reply)
			throws UnreadableException, IOException {
		DogAgent a = (DogAgent) myAgent;

		ScareRequestMessage srm = (ScareRequestMessage) msg.getContentObject();
		if (srm != null) {
			logger.debug("{} : jestem najlepszy!", a.getName());
			long timeout;
			if (srm.p != null) { // czyli jestem liderem i musze obliczyc czas
				timeout = computeTimeout(srm.p);
				if (a.isLeader()) {
					broadcastScareRequest(timeout);
				}
			} else {
				timeout = srm.timeout;
			}
			scareDeer(timeout);
			a.activate();
		}

		return false;
	}

	private long computeTimeout(Position hunters) {
		DogAgent a = (DogAgent) myAgent;

		Position dog = a.getPosition();
		Position deer = PositionHelper.getDeerPosition();

		float dogDeerDist = PositionHelper.getDistance(dog, deer);
		float hunterDeerDist = PositionHelper.getDistance(hunters, deer);
		float dogHunterDist = PositionHelper.getDistance(dog, hunters);

		float fleeDist = ((dogHunterDist * dogHunterDist)
				- (hunterDeerDist * hunterDeerDist) - (dogDeerDist * dogDeerDist))
				/ (2 * dogDeerDist);
		
		long maxTimeout = (long) fleeDist * Time.DEER_STEP_MIN + System.currentTimeMillis();
		long nextDeerPositionUpdate = a.getLastDeerPositionUpdate() + Time.HELICOPTER_BROADCAST; 
		
		return Math.min(maxTimeout, nextDeerPositionUpdate);
	}

	private void broadcastScareRequest(long timeout) throws IOException {
		DogAgent a = (DogAgent) myAgent;

		ACLMessage msg = new ACLMessage(ACLMessage.REQUEST);
		msg.setConversationId(DogAgent.SCARE_CONV);
		msg.setContentObject(new ScareRequestMessage(a.getAID(), timeout));

		for (AID aid : a.getGroup()) {
			msg.addReceiver(aid);
		}

		a.send(msg);
	}

	private void scareDeer(long timeout) throws IOException {
		DogAgent a = (DogAgent) myAgent;

		ACLMessage msg = new ACLMessage(ACLMessage.INFORM);
		msg.setConversationId(DogAgent.SCARE_CONV);
		msg.setContentObject(new ScareMessage(a.getAID(), a.getPosition(), timeout));

		// szukam losia
		DFAgentDescription template = new DFAgentDescription();
  		ServiceDescription sd = new ServiceDescription();
  		sd.setType(DeerAgent.SERVICE_TYPE);
  		template.addServices(sd);
  		
  		SearchConstraints sc = new SearchConstraints();
  		sc.setMaxResults(1l);
  		  		
		try {
			DFAgentDescription[] results = DFService.search(a, template, sc);
			if (results.length > 0) {
	  			msg.addReceiver(results[0].getName());
	  		}
		} catch (FIPAException e) {
			logger.error("{} : błąd FIPA", a.getName(), e);
		}
		// wysylam
		a.send(msg);
	}

}
